/**
 * Collaborative Story Development Web Application (CSDApp) Copyright Anton
 * Strack 2014
 *
 * This file is part of Collaborative Story Development Application (CSDApp).
 *
 * CSDApp is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or any later version.
 *
 * CSDApp is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * CSDApp. If not, see <http://www.gnu.org/licenses/>.
 *
 */
package csdwa;

import java.sql.Connection;
import java.sql.Timestamp;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * The controller class handles user initiated requests for the particular
 * resource and prepares the correct view.
 *
 * @author Anton Strack
 */
public class CategoryController extends ResourceController {

    private final String VIEW_LIST = "/category/index.jsp";
    private final String VIEW_VIEW = "/category/view.jsp";
    private final String VIEW_CREATE = "/category/create.jsp";
    private final String VIEW_UPDATE = "/category/update.jsp";
    private final String VIEW_DELTE = "/category/delete.jsp";
    private final String VIEW_ADD = "/category/add.jsp";
    private final String VIEW_LIST_POSTS = "/category/listPosts.jsp";

    public CategoryController(Connection conn) {
        super(conn);
    }

    /**
     * This method primarily handles form related requests.
     *
     * @param request the request object
     * @return an updated request object sufficient to complete the request.
     */
    @Override
    public CsdwaRequest handlePostRequest(CsdwaRequest request) {
        String form = request.getFormId();
        String primaryAction = request.getPrimaryActionTypeName();
        String primaryResource = request.getPrimaryResourceTypeName();
        String postId = request.getParameter("secondaryId");
        request.setPrimaryResourceId(postId);
        int id = request.getPrimaryResourceId();
        if (form.isEmpty() == false) {
            request.setPrimaryResourceId(request.getParameter("postId"));
            if (form.equalsIgnoreCase("create-category")) {
                request.removeFormId();
                request = handleCreate(request);
            } else if (form.equalsIgnoreCase("update-category")) {
                request.removeFormId();
                request = handleUpdate(request);
            } else if (form.equalsIgnoreCase("delete-category")) {
                request.removeFormId();
                request = handleDelete(request);
            } else if (form.equalsIgnoreCase("add-category")) {
                request.removeFormId();
                request = handleAdd(request);
            }
        }
        return request;
    }

    /**
     * This method handles get requests. It accepts the primaryResouce and
     * primaryAction as variables to allow routing from the handleGetRequest
     * method.
     *
     * @param request the request object
     * @return an updated request object sufficient to complete the request.
     */
    @Override
    public CsdwaRequest handleGetRequest(CsdwaRequest request) {
        String primaryAction = request.getPrimaryActionTypeName();
        String primaryResource = request.getPrimaryResourceTypeName();
        String secondaryResource = request.getSecondaryResourceTypeName();
        String secondaryAction = request.getSecondaryActionTypeName();
        int secondaryId = request.getSecondaryResourceId();
        int id = request.getPrimaryResourceId();
        if ("category".equalsIgnoreCase(primaryResource)) {
            if ("view".equalsIgnoreCase(primaryAction) && id != 0) {

                CategoryDao cdao = new CategoryDao(conn);
                cdao.setUseRelatedMarkers(true);
                int result = cdao.selectBeanById(id);
                if (result > 0) {
                    request.setBean(cdao.getBean());
                    request.setView(VIEW_VIEW);
                } else {
                    request.setMessage("The category couldn't be found.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("list");
                    request.setPrimaryResourceTypeName("category");
                    request.setPrimaryResourceId(0);
                    request = handleGetRequest(request);
                }
                //  this.getResourceRequestDelegator().getPostHistoryManager().recordUserPostHistory(request.getCurrentUser(), getResourceRequestDelegator().getActionId("view"), tdao.getBean());
            } else if ("update".equalsIgnoreCase(primaryAction) && id != 0) {
                // Connection conn = this.conn;
                CategoryDao cdao = new CategoryDao(conn);
                cdao.selectBeanById(id);
                /* Store the bean in a session so that the it is available to the jsp page and also post update handler on post submit. This way it can compare the post bean values to the original bean values. Doing so will mean that only the columns changed will be sent to the database to be updated.
                 */
                int result = cdao.selectBeans();
                if (result > 0) {
                    request.setView(VIEW_UPDATE);
                    request.removeSessionAttribute("bean");
                    request.setSessionAttribute("bean", cdao.getBean());
                } else {
                    request.setMessage("The category couldn't be found.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("list");
                    request.setPrimaryResourceTypeName("category");
                    request.setPrimaryResourceId(0);
                    request = handleGetRequest(request);
                    Logger.getLogger(TaskController.class.getName()).log(Level.WARNING, "No action type was detected for category.");
                }
            } else if ("create".equalsIgnoreCase(primaryAction)) {
                request.setView(VIEW_CREATE);
                //if categories can have a parent category then need to query categories and send them as beans.

            } else if ("delete".equalsIgnoreCase(primaryAction)) {
                //  Connection conn = this.conn.getConnection();
                CategoryDao cdao = new CategoryDao(conn);
                int result = cdao.selectBeanById(id);
                // will need to delete all category mappings to plots

                if (result > 0) {
                    request.setBean(cdao.getBean());
                    request.setView(VIEW_DELTE);
                } else {
                    request.setMessage("The category couldn't be found.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("list");
                    request.setPrimaryResourceTypeName("category");
                    request.setPrimaryResourceId(0);
                    request = handleGetRequest(request);
                    Logger.getLogger(TaskController.class.getName()).log(Level.SEVERE, "No category found in the database.");
                }
            } else if ("add".equalsIgnoreCase(primaryAction) && secondaryResource != null && secondaryResource.isEmpty() == false && id != 0) {
                // action=add&resource=category&for=plot&id=###
                CategoryDao cdao = new CategoryDao(conn);
                int result = cdao.selectBeans();
                System.out.println("category count is:" + result);
                if (result > 0) {
                    request.setBeans(cdao.getBeans());
                    int resourceTypeId = InfoAggregator.getInstance().getResourceTypeId(secondaryResource);//this.ResourceRequestDelegator.getResourceId(secondaryResource);
                    CsdwaDao dao = new DaoFactory(conn).getDao(resourceTypeId);
                    int pResult = 0;
                    if (dao != null) {
                        pResult = dao.selectBeanById(id);
                    }
                    if (pResult > 0) {
                        request.setAttribute("posts", dao.getBeans()); // might make it so multiple posts of the same type can be selected.
                        request.setAttribute("postTypeName", secondaryResource);
                        request.setAttribute("postTypeId", resourceTypeId);
                        request.setView(VIEW_ADD);
                    } else {
                        request.setMessage("The post couldn't be found.");
                        request.setMessageType("error");
                        request.setPrimaryActionTypeName("list");
                        request.setPrimaryResourceTypeName("category");
                    }
                } else {
                    request.setMessage("No categories were found.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("list");
                    request.setPrimaryResourceTypeName("category");
                    request.setPrimaryResourceId(0);
                    Logger.getLogger(TaskController.class.getName()).log(Level.SEVERE, "No category found in the database.");
                    request = handleGetRequest(request);
                }
            } else if ("remove".equalsIgnoreCase(primaryAction) && secondaryResource != null && secondaryResource.isEmpty() == false && id != 0 && secondaryId != 0) {
                // start a task and thus track it.
                //  Connection conn = this.conn.getConnection();
                int postResourceTypeId = InfoAggregator.getInstance().getResourceTypeId(secondaryResource);//this.ResourceRequestDelegator.getResourceId(secondaryResource);
                CategoryPostMapperDao cpmDao = new CategoryPostMapperDao(conn, postResourceTypeId);
                CategoryPostMap cpm = new CategoryPostMap();
                CategoryDao cdao = DaoFactory.makeCategoryDao(conn);
                int result = 0;
                if (cdao != null) {
                    result = cdao.selectBeanById(id);
                }

                if (result > 0) {
                    cpm.setCategoryId(id);
                    cpm.setPostId(secondaryId);
                    int cpmResult = cpmDao.deleteBean(cpm);
                    if (cpmResult > 0) {
                        Category category = cdao.getBean();
                        int oldFrequency = category.getFrequency();
                        category.setFrequency(category.getFrequency() - 1);
                        int frequency = cdao.updateBean(category);
                        request.setMessage("The category was removed successfully.");
                        request.setMessageType("success");
                        request.setPrimaryActionTypeName("view");
                        request.setPrimaryResourceTypeName(secondaryResource);
                        request.setPrimaryResourceId(secondaryId);
                        request = this.ResourceRequestDelegator.processRequest(request);

                    } else {
                        request.setMessage("The category couldn't be deleted.");
                        request.setMessageType("error");
                        request.setPrimaryActionTypeName("view");
                        request.setPrimaryResourceTypeName(secondaryResource);
                        request.setPrimaryResourceId(secondaryId);
                        Logger.getLogger(CategoryController.class.getName()).log(Level.SEVERE, "Couldn't delete category mapping to post.");
                        request = this.ResourceRequestDelegator.processRequest(request);
                    }
                } else {
                    request.setMessage("The category couldn't be found.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("view");
                    request.setPrimaryResourceTypeName(secondaryResource);
                    request.setPrimaryResourceId(secondaryId);
                    Logger.getLogger(CategoryController.class.getName()).log(Level.SEVERE, "No category found in the database.");
                    request = this.ResourceRequestDelegator.processRequest(request);
                }
            } else if ("list".equalsIgnoreCase(primaryAction) && secondaryResource != null && secondaryResource.isEmpty() == false && id != 0) {
                //list categories for the specific post
            } else if ("list".equalsIgnoreCase(primaryAction) && id != 0) {
                //list all posts belonging to the category
                //  Connection conn = this.conn.getConnection();
                CategoryDao cdao = DaoFactory.makeCategoryDao(conn);
                int result = cdao.selectBeanById(id);
                if (result > 0) {
                    request.setBean(cdao.getBean());
                    CategoryPostMapperDao plotCmDao = new CategoryPostMapperDao(conn, InfoAggregator.getInstance().getResourceTypeId("plot"));
                    plotCmDao.selectPostsByCategoryId(id, true);
                    request.setAttribute("plotCategories", plotCmDao.getPostDao().getBeans());
                    CategoryPostMapperDao characterCmDao = new CategoryPostMapperDao(conn, InfoAggregator.getInstance().getResourceTypeId("character"));
                    characterCmDao.selectPostsByCategoryId(id, true);
                    request.setAttribute("characterCategories", characterCmDao.getPostDao().getBeans());
                    CategoryPostMapperDao dialogCmDao = new CategoryPostMapperDao(conn, InfoAggregator.getInstance().getResourceTypeId("dialog"));
                    dialogCmDao.selectPostsByCategoryId(id, true);
                    request.setAttribute("dialogCategories", dialogCmDao.getPostDao().getBeans());
                    CategoryPostMapperDao locationCmDao = new CategoryPostMapperDao(conn, InfoAggregator.getInstance().getResourceTypeId("location"));
                    locationCmDao.selectPostsByCategoryId(id, true);
                    request.setAttribute("locationCategories", locationCmDao.getPostDao().getBeans());
                    // need to add additional post types like above
                    request.setView(VIEW_LIST_POSTS);

                } else {
                    request.setMessage("The category couldn't be found.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("view");
                    request.setPrimaryResourceTypeName(secondaryResource);
                    request.setPrimaryResourceId(secondaryId);
                    Logger.getLogger(CategoryController.class.getName()).log(Level.SEVERE, "No category found in the database.");
                    request = this.ResourceRequestDelegator.processRequest(request);
                }
            } else {// index by default
                request.setView(VIEW_LIST);
                CategoryDao cdao = new CategoryDao(conn);
                cdao.setUseRelatedMarkers(true);
                int result = cdao.selectBeans();

                if (result > 0) {
                    request.setBeans(cdao.getBeans());
                } else {
                    request.setMessage("There are no categories to list.");
                    request.setMessageType("info");
                }
            }
        }
        return request;
    }

    /**
     * This method saves the bean to the database.
     *
     * @param request the create request object containing the form values.
     * @return the request object with the requestResultMessage object set to
     * indicate the result
     */
    private CsdwaRequest handleCreate(CsdwaRequest request) {
        User user = (User) request.getSessionAttribute("user");
        Timestamp timestamp = new Timestamp(new Date().getTime());
        // Create validators to use for validation
        CsdwaValidator isNotEmpty = new IsNotEmpty();
        // add request parameter values to the object to test validation
        ValidatorValue name = new ValidatorValue(request.getParameter("name"), "Name");
        ValidatorValue description = new ValidatorValue(request.getParameter("description"), "Description");
        // the the validators to the validation values
        name.addValidator(isNotEmpty);
        description.addValidator(isNotEmpty);
        // isValid the values.
        if (name.isValid() && description.isValid()) {

            Category bean = ResourceFactory.makeCategory();
            bean.setName(name.getString());
            bean.setDescription(description.getString());
            bean.setCreatedById(user.getId());
            bean.setCreatedDate(timestamp);

            CategoryDao cdao = new CategoryDao(conn);
            int result = cdao.insertBean(bean);
            if (result > 0) {
                bean.setId(result);
                request.setMessage("The category was successfully created.");
                request.setMessageType("success");
                request.setPrimaryActionTypeName("view");
                request.setPrimaryResourceTypeName("category");
                request.setPrimaryResourceId(result);
                request = handleGetRequest(request);
            } else {
                request.setMessage("There was an error saving the category to the database.");
                request.setMessageType("error");
                request.setPrimaryActionTypeName("create");
                request.setPrimaryResourceTypeName("category");
                request.setPrimaryResourceId(0);
                request = handleGetRequest(request);
            }
        } else {
            request.setMessage(name.getErrorMessage() + description.getErrorMessage());
            request.setMessageType("info");
            request.setPrimaryActionTypeName("create");
            request.setPrimaryResourceTypeName("category");
            request.setPrimaryResourceId(0);
            request = handleGetRequest(request);
        }
        return request;
    }

    /**
     * This method handles updating the bean. If the bean to be updated was
     * properly set as a bean in the session, then this method will instruct the
     * dao to update only the fields that were changed and thus reduce message
     * size.
     *
     * @param request the update request object
     * @return a request object with the requestResultMessage object set to
     * indicate the update result.
     */
    private CsdwaRequest handleUpdate(CsdwaRequest request) {
        User user = (User) request.getSessionAttribute("user");
        Timestamp timestamp = new Timestamp(new Date().getTime());
        Category originalPost = (Category) request.getSessionAttribute("bean");

        CsdwaValidator isNotEmpty = new IsNotEmpty();
        // add request parameter values to the object to test validation
        ValidatorValue name = new ValidatorValue(request.getParameter("name"), "Name");
        ValidatorValue description = new ValidatorValue(request.getParameter("description"), "Description");
        // the the validators to the validation values
        name.addValidator(isNotEmpty);
        description.addValidator(isNotEmpty);
        // isValid the values.
        if (name.isValid() && description.isValid()) {
            CategoryDao cdao = new CategoryDao(conn);
            StringBuilder sb = new StringBuilder();
            if (originalPost == null) {
                originalPost = ResourceFactory.makeCategory();// if the original post didn't get stored in the session, then create a new one.
            }

            if (originalPost.getName().equals(name.getString()) == false) {
                sb.append("Name");
            }
            if (originalPost.getDescription().equals(description.getString()) == false) {
                if (sb.length() > 0) {
                    sb.append(", ");
                }
                sb.append("Description");
            }
            // update the original bean with all the values from the form submit
            originalPost.setName(name.getString());
            originalPost.setDescription(description.getString());
            // declare the columns to update.
            cdao.setColumnsToUpdate(sb.toString());

            int result = cdao.updateBean(originalPost);
            request.removeSessionAttribute("bean");
            if (result >= 0) {
                request.setMessage("The category was successfully updated.");
                request.setMessageType("success");
                request.setPrimaryActionTypeName("view");
                request.setPrimaryResourceTypeName("category");
                request.setPrimaryResourceId(originalPost.getId());
                request = handleGetRequest(request);
            } else {
                request.setMessage("There was an error trying to update the category to the database.");
                request.setMessageType("error");
                request.setPrimaryActionTypeName("update");
                request.setPrimaryResourceTypeName("category");
                request.setPrimaryResourceId(originalPost.getId());
                request = handleGetRequest(request);
            }
        } else {
            request.setMessage(name.getErrorMessage() + description.getErrorMessage());
            request.setMessageType("info");
            request.setPrimaryActionTypeName("update");
            request.setPrimaryResourceTypeName("category");
            request.setPrimaryResourceId(originalPost.getId());
            request = handleGetRequest(request);
        }

        return request;
    }

    /**
     * This method handles deleting the specific bean from the database. This
     * method handles the cascading deletes of all dependant resources.
     *
     * @param request the delete request object.
     * @return a request object with a requestResultMessage object specifying
     * the result.
     */
    private CsdwaRequest handleDelete(CsdwaRequest request) {
        // validate fields
        int id = request.getPrimaryResourceId();
        boolean allowDelete = false;
        if (id > 0) {
            try {
                // Connection conn = this.conn.getConnection();
                //  conn.setAutoCommit(false);// enter transaction mode //- not sure if need transaction mode for categories yet.
                // Savepoint delete = conn.setSavepoint("delete-category_" + id);
                CategoryDao cdao = new CategoryDao(conn);
                if (cdao.selectBeanById(id) > 0) {
                    allowDelete = true;
                }
                if (allowDelete == true) {
                    Category category = cdao.getBean();
                    int result = cdao.deleteBean(category);
                    request.removeAttribute("formSubmit");
                    if (result > 0) {
                        //   conn.setAutoCommit(true);// reset commit mode to auto and commit the transaction since the deletion worked correctly.
                        request.setMessage("The category was successfully deleted from the site.");
                        request.setMessageType("success");
                        request.setPrimaryActionTypeName("list");
                        request.setPrimaryResourceTypeName("category");
                        request.setPrimaryResourceId(0);
                        request = handleGetRequest(request);
                    } else {
                        //   conn.rollback(delete); // delete failed so roll back connection
                        //    conn.setAutoCommit(true);// reset commit mode to auto
                        request.setMessage("There was an error trying to delete the category from the database.");
                        request.setMessageType("error");
                        request.setPrimaryActionTypeName("view");
                        request.setPrimaryResourceTypeName("category");
                        request.setPrimaryResourceId(id);
                        request = handleGetRequest(request);
                    }

                } else {
                    //   conn.rollback(delete); // delete failed so roll back connection
                    //   conn.setAutoCommit(true);// reset commit mode to auto
                    request.setMessage("A dependant resource could not be deleted. Deletion aborted.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("view");
                    request.setPrimaryResourceTypeName("category");
                    request.setPrimaryResourceId(0);
                    request = handleGetRequest(request);
                }

            } catch (Exception ex) { //SQLException ex) {
                Logger.getLogger(TaskController.class
                        .getName()).log(Level.SEVERE, null, ex);
            }
        } else {
            request.setMessage("There was an error with the delete request. Could not determine the category ID.");
            request.setMessageType("error");
            request.setPrimaryActionTypeName("list");
            request.setPrimaryResourceTypeName("category");
            request.setPrimaryResourceId(0);
            request = handleGetRequest(request);
        }
        return request;
    }

    /**
     * This method saves the bean to the database.
     *
     * @param request the create request object containing the form values.
     * @return the request object with the requestResultMessage object set to
     * indicate the result
     */
    private CsdwaRequest handleAdd(CsdwaRequest request) {
        // Create validators to use for validation
        CsdwaValidator isNotEmpty = new IsNotEmpty();
        CsdwaValidator isNumeric = new IsNumeric();
        // add request parameter values to the object to test validation
        ValidatorValue categories = new ValidatorValue(request.getParameters("categories"), "Category");
        ValidatorValue posts = new ValidatorValue(request.getParameters("postId"), "postId");
        ValidatorValue postTypeId = new ValidatorValue(request.getParameter("postTypeId"), "Post Type ID");
        // the the validators to the validation values
        categories.addValidator(isNotEmpty).addValidator(isNumeric);
        posts.addValidator(isNotEmpty).addValidator(isNumeric);
        postTypeId.addValidator(isNotEmpty).addValidator(isNumeric);
        // isValid the values.
        if (categories.isValid() && posts.isValid() && postTypeId.isValid()) {
            CategoryPostMap categoryPostMap;
            int result = 0;
            // for each post id
            int lastPostId = 0;
            CategoryPostMapperDao cpmDao = new CategoryPostMapperDao(this.conn, postTypeId.getInteger());// need to select the proper dao per post type
            CsdwaDao<Category> cdao = cpmDao.getCategoryDao();
            Category category;
            for (String postId : posts.getList()) {
                lastPostId = Integer.parseInt(postId);
                categoryPostMap = new CategoryPostMap();
                categoryPostMap.setPostId(Integer.parseInt(postId));
                for (String categoryId : categories.getList()) {
                    categoryPostMap.setCategoryId(Integer.parseInt(categoryId));
                    result = cpmDao.insertBean(categoryPostMap);
                    int updateFrequency = cdao.selectBeanById(Integer.parseInt(categoryId));
                    category = cdao.getBean();
                    category.setFrequency(category.getFrequency() + 1);// this frequency isn't being calculated correctly
                    int updateResult = cdao.updateBean(category);
                }
            }
            if (result > 0) {
                request.setMessage("The category was successfully added to the " + InfoAggregator.getInstance().getResourceTypeName(postTypeId.getInteger()) + ".");
                request.setMessageType("success");
                request.setPrimaryActionTypeName("view");
                request.setPrimaryResourceTypeName(InfoAggregator.getInstance().getResourceTypeName(postTypeId.getInteger()));
                request.setPrimaryResourceId(lastPostId);
                request.setMethod("GET");
                request = this.ResourceRequestDelegator.processRequest(request);
            } else {
                request.setMessage("There was an error adding the category to the " + InfoAggregator.getInstance().getResourceTypeName(postTypeId.getInteger()) + ".");
                request.setMessageType("error");
                request.setPrimaryActionTypeName("view");
                request.setPrimaryResourceTypeName(InfoAggregator.getInstance().getResourceTypeName(postTypeId.getInteger()));
                request.setPrimaryResourceId(lastPostId);
                request.setMethod("GET");
                request = this.ResourceRequestDelegator.processRequest(request);
            }
        } else {
            request.setMessage(categories.getErrorMessage() + posts.getErrorMessage() + postTypeId.getErrorMessage());
            request.setMessageType("info");
            request.setPrimaryActionTypeName("create");
            request.setPrimaryResourceTypeName("category");
            request.setPrimaryResourceId(0);
            request = handleGetRequest(request);
        }
        return request;
    }
}
